﻿using ARchGL.Platform.Configuration;
using ARchGL.Platform.Web;
using Microsoft.Extensions.Configuration;
using Npgsql;
using NpgsqlTypes;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ARchGL.Platform.Utils
{
    /// <summary>
    /// 实体批量插入类
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public static class BulkInsert<TEntity>
    {
        /// <summary>
        /// 实体表
        /// </summary>
        private static Dictionary<Type, object> TableNameDict = new Dictionary<Type, object>();

        /// <summary>
        /// 字符串配置
        /// </summary>
        private static IConfigurationRoot configuration = AppConfigurations.Get(WebContentDirectoryFinder.GetContentRootFolder(),
#if DEBUG
            "Development",
#endif
            addUserSecrets: true);

        /// <summary>
        /// 插入整个实体到数据库
        /// </summary>
        /// <param name="list"></param>
        public static void BulkEntityInsert(List<TEntity> list)
        {
            var PropNames = new List<string>();
            var typeArgs = GetPropertyFromTEntity();
            foreach (PropertyInfo tParam in typeArgs)
            {
                PropNames.Add(tParam.Name);
            }
            var copySql = CreatSQL(PropNames);
            Insert(copySql, list, typeArgs);
        }

        /// <summary>
        /// 选择属性插入数据库
        /// </summary>
        /// <param name="list"></param>
        /// <param name="expression"></param>
        public static void BulkEntityInsert(List<TEntity> list, params Expression<Func<TEntity, object>>[] expression)
        {
            if (!list.Any()) return;
            var proper = new List<string>();
            foreach (var param in expression)
            {
                var body = param.Body;
                if (body.Type == typeof(string))
                {
                    var prop = body.ToString();
                    proper.Add(prop.Substring(prop.IndexOf(".") + 1));
                }
                else
                {
                    var prop = body.ToString();
                    if (prop.IndexOf(",") != -1)
                    {
                        proper.Add(prop.Substring(0, prop.IndexOf(",")).Split(".")[1]);
                    }
                    else
                    {
                        proper.Add(prop.Split(".")[1]);
                    }
                }
            }
            var copySql = CreatSQL(proper);
            var propertyInfoList = new List<PropertyInfo>();
            foreach (var prop in proper)
            {
                propertyInfoList.Add(GetPropertyFromTEntity(prop));
            }
            Insert(copySql, list, propertyInfoList.ToArray());
        }

        #region Private method

        /// <summary>
        /// 获取数据库实体表名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static object GetTableName<T>()
        {
            var type = typeof(T);
            if (TableNameDict.ContainsKey(type)) return TableNameDict[type];

            else
            {
                var attr = type.GetCustomAttributes().FirstOrDefault(x => x.GetType() == typeof(TableAttribute)) as TableAttribute;
                TableNameDict.Add(type, attr?.Name);
                return attr?.Name;
            }
        }

        /// <summary>
        /// 获取TEntity的属性信息
        /// </summary>
        /// <returns>TEntity的属性信息的列表</returns>
        private static PropertyInfo[] GetPropertyFromTEntity()
        {
            var t = typeof(TEntity);
            return t.GetProperties();
        }

        /// <summary>
        /// 获取TEntity的属性信息
        /// </summary>
        /// <returns>TEntity的属性信息的列表</returns>
        private static PropertyInfo GetPropertyFromTEntity(string name)
        {
            var t = typeof(TEntity);
            return t.GetProperty(name);
        }

        /// <summary>
        /// 创建sql语句
        /// </summary>
        /// <param name="paramas"></param>
        /// <returns></returns>
        private static string CreatSQL(List<string> paramas)
        {
            var tableName = GetTableName<TEntity>().ToString();
            return $"COPY \"{tableName}\"(\"{string.Join("\",\"", paramas)}\") FROM STDIN (FORMAT BINARY)";
        }

        /// <summary>
        /// 批量插入pgsql数据库
        /// </summary>
        /// <param name="copySql"></param>
        /// <param name="list"></param>
        /// <param name="typeArgs"></param>
        private static void Insert(string copySql, List<TEntity> list, PropertyInfo[] typeArgs)
        {
            using (var conn = new NpgsqlConnection(configuration.GetConnectionString(TenantConsts.ConnectionStringName)))
            {
                conn.Open();
                using (var writer = conn.BeginBinaryImport(copySql))
                {
                    foreach (var wrapper in list)
                    {
                        writer.StartRow();
                        foreach (PropertyInfo pi in typeArgs)
                        {
                            if (pi.PropertyType.BaseType == typeof(System.Enum))
                            {
                                writer.Write(pi.GetValue(wrapper).GetHashCode(), NpgsqlDbType.Integer);
                                continue;
                            }
                            writer.Write(pi.GetValue(wrapper));
                        }
                    }
                    writer.Complete();
                }
            }
        }

        #endregion
    }
}
